import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# plt.style.use('seaborn-whitegrid')
raw_data = {
    'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
    'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'],
    'age': [42, 52, 36, 24, 73],
    'preTestScore': [4, 24, 31, 2, 3],
    'postTestScore': [25, 94, 57, 62, 70]}
# df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df = pd.DataFrame(raw_data, columns=['age', 'preTestScore', 'postTestScore'])
print(df)


# 循环访问行，对于每一行，我们查询数据记录以查看有多少行与准标识符的值匹配。
# 如果任何组中的行数小于k，则数据帧不满足该值k-Anonymity，我们返回 False。
# 请注意，在这个简单的定义中，我们认为所有列都包含准标识符;要将检查限制为所有列的子集，我们需要将表达式替换为其他内容。
# 当k=1的时候是满足条件的返回true
def isKAnonymized(df, k):
    for index, row in df.iterrows():
        query = ' & '.join([f'{col} == {row[col]}' for col in df.columns])
        rows = df.query(query)
        if rows.shape[0] < k:  # len(rows)
            return False
    return True


print(isKAnonymized(df, 1))


# isKAnonymized(df, 2)


# 这个函数里面的y表示表中实际数据，比如42。depths[x.name]表示当前属性下设定的映射值，下面在depths中将各个属性设为1，也就是对42/10再*10得到40。整个函数就是对所有数字向下取到10的倍数。
def generalize(df, depths):
    return df.apply(lambda x: x.apply(lambda y: int(int(y / (10 ** depths[x.name])) * (10 ** depths[x.name]))))


# 尝试将每列概括为一个"级别" ， 即舍入到最接近的10。
depths = {
    'age': 1,
    'preTestScore': 1,
    'postTestScore': 1
}
df2 = generalize(df, depths)
print(df2)
print(isKAnonymized(df2, 2))

# 可以尝试泛化更多，但随后我们最终会删除所有数据！各个属性的深度又1变成2，等于是42/100直接为0了。
depths = {
    'age': 2,
    'preTestScore': 2,
    'postTestScore': 2
}
df2 = generalize(df, depths)
print(df2)
print(isKAnonymized(df2, 2))

'''
   age  preTestScore  postTestScore
0   42             4             25
1   52            24             94
2   36            31             57
3   24             2             62
4   73             3             70
true
   age  preTestScore  postTestScore
0   40             0             20
1   50            20             90
2   30            30             50
3   20             0             60
4   70             0             70
False
   age  preTestScore  postTestScore
0    0             0              0
1    0             0              0
2    0             0              0
3    0             0              0
4    0             0              0
true
'''